/* 
    sample code for JPEG for 7113 composite input and JPEG output
    This sample code is to do encode five JPEG files named "/tmp/img0.jpg"...
 */
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/timeb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/ipc.h>
#include <sys/socket.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <linux/videodev.h>
#include <errno.h>
#include <linux/fb.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/un.h>
#include <signal.h>
#include "fmjpeg_avcodec.h"

#define FMJPEG_ENCODER_DEV  "/dev/mjenc" //major:10 minior:61
#define dout_name "/tmp/dev0.jpg"
FILE   *dout, *din;

typedef struct AVFrame {
    uint8_t *data[4];
} AVFrame;


typedef struct video_profile
{
    unsigned int bit_rate;
    unsigned int width;   //length per dma buffer
    unsigned int height;
    unsigned int framerate;
    unsigned int frame_rate_base;
    unsigned int gop_size;
    unsigned int qmax;
    unsigned int qmin;   
    unsigned int quant;
    unsigned int fmjpeg_image_quality;
    unsigned int u32RestartInterval;
    unsigned int fmjpeg_jpeg;
    unsigned int fmjpeg_yuv_format;
    int roi_enable;
    unsigned int roi_left_x;
    unsigned int roi_left_y;
    unsigned int roi_right_x;
    unsigned int roi_right_y;
    AVFrame *coded_frame;
    char *priv;
} video_profile;

video_profile           video_setting;	
FJPEG_ENC_PARAM enc_param;
unsigned char           *video_buf_virt;
unsigned char           *out_virt_buffer;
int				mmap_addr;
int fmjpeg_enc_fd=0;


#define TEST_WIDTH 704
#define TEST_HEIGHT 480
#define TEST_ROUND 100

#define IMAGE_COMP		3
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define MIN(x, y) ((x) < (y) ? (x) : (y))

#define SET_COMP(index,hsamp,vsamp)  \
  (enc_param.rgComponentInfo[index].m_u8HSamplingFrequency = (hsamp), \
   enc_param.rgComponentInfo[index].m_u8VSamplingFrequency = (vsamp))

typedef unsigned long long uint64;

struct timeval t1;
uint64  nsecs;
void time_start(struct timeval *tv)
{
    gettimeofday (tv, NULL);
}

uint64 time_delta(struct timeval *start, struct timeval *stop)
{
    uint64 secs, usecs;
    
    secs = stop->tv_sec - start->tv_sec;
    usecs = stop->tv_usec - start->tv_usec;
    if (usecs < 0)
    {
        secs--;
        usecs += 1000000;
    }
    return secs * 1000000 + usecs;
}

uint64 time_stop(struct timeval *tv)
{
    struct timeval end;
    gettimeofday (&end, NULL);
    return time_delta (tv, &end);
}


int mjpeg_init(video_profile *video_setting)
{
        	unsigned int    yuv_size=0;  
  	unsigned int    max_h_samp,max_v_samp;
  	int             i;
  	int             YUVsampling;
         unsigned int    image_size[3];
    
         
   	fmjpeg_enc_fd=open(FMJPEG_ENCODER_DEV,O_RDWR);
	    	
	if(fmjpeg_enc_fd==0)  {
	    printf("Fail to open %s\n",FMJPEG_ENCODER_DEV);
	    fflush(stdout);
	  	return -1;
	} 

  	enc_param.u32ImageQuality=video_setting->fmjpeg_image_quality; // we set image quality to 90 (0~100)  
  	enc_param.u32RestartInterval=video_setting->u32RestartInterval; // we set restart interval to 5  
 	enc_param.u32ImageWidth=video_setting->width;  // set image width
  	enc_param.u32ImageHeight=video_setting->height; // set image height
  	enc_param.u8NumComponents=IMAGE_COMP; // the input image has 3 components 'YUV'  
         enc_param.u8JPGPIC=video_setting->fmjpeg_jpeg;
         enc_param.u82D=0;    // 0: for Mp4 1: for JPG 2: for H.264
         enc_param.roi_enable=0;	
         enc_param.u32ImageMotionDetection=0;

  	//YUVsampling = 0; // YUV420
  	//YUVsampling = 1; // YUV422
  	//YUVsampling = 2; // YUV211
  	//YUVsampling = 3; // YUV333
  	//YUVsampling = 4; // YUV222
  	//YUVsampling = 5; // YUV111
        YUVsampling=video_setting->fmjpeg_yuv_format;

	if(enc_param.u8NumComponents==1)
        YUVsampling = 5; // if there is only one component, it is gray, so we force it YUV111
    
	switch (YUVsampling) {
		case 0:
		  SET_COMP(0, 2, 2);
		  SET_COMP(1, 1,1);
		  SET_COMP(2, 1,1);
		  break;
		case 1:
		  SET_COMP(0, 4,1);
		  SET_COMP(1, 2,1);
		  SET_COMP(2, 2,1);
		  break;
		case 2:
		  SET_COMP(0, 2,1);
		  SET_COMP(1, 1,1);
		  SET_COMP(2, 1,1);
		  break;
		case 3:
		  SET_COMP(0, 3,1);
		  SET_COMP(1, 3,1);
		  SET_COMP(2, 3,1);
		  break;
		case 4:
		  SET_COMP(0, 2,1);
		  SET_COMP(1, 2,1);
		  SET_COMP(2, 2,1);
		  break;
		case 5:
		  SET_COMP(0, 1,1);
		  SET_COMP(1, 1,1);
		  SET_COMP(2, 1,1);
		  break;
		default:
		  break;
	}  // to set each component's sampling factor (horizontally and vertically)
  	   
  	// get the maximum horizontal sampling factor
  	max_h_samp=MAX(enc_param.rgComponentInfo[0].m_u8HSamplingFrequency,
             	MAX(enc_param.rgComponentInfo[1].m_u8HSamplingFrequency,
                 enc_param.rgComponentInfo[2].m_u8HSamplingFrequency));
  	// get the maximum horizontal sampling factor
  	max_v_samp=MAX(enc_param.rgComponentInfo[0].m_u8VSamplingFrequency,
             	MAX(enc_param.rgComponentInfo[1].m_u8VSamplingFrequency,
                 enc_param.rgComponentInfo[2].m_u8VSamplingFrequency)); 
                 
         
  	// calculate each component size according to its maximum sampling factor
  	// and individual sampling factor
  	for(i=0;i<enc_param.u8NumComponents;i++) {
	    image_size[i]=(((enc_param.rgComponentInfo[i].m_u8HSamplingFrequency*enc_param.u32ImageWidth) /max_h_samp) *
                    ((enc_param.rgComponentInfo[i].m_u8VSamplingFrequency*enc_param.u32ImageHeight) /max_v_samp));
            yuv_size += image_size[i];
        }

  	if ( ioctl(fmjpeg_enc_fd,FMJPEG_IOCTL_ENCODE_CREATE,&enc_param) < 0 ) {
	        printf("Error to set FMJPEG_IOCTL_ENCODE_CREATE\n"); 
	        fflush(stdout);	
	        return -1;
  	}
  	return 0;
}

int mjpeg_encode(video_profile *video_setting,unsigned char *buf, void *data)
{
      	AVFrame         *pav=(AVFrame *)data;  
  	int             i;
  
	for(i=0;i<enc_param.u8NumComponents;i++)
             enc_param.pu8YUVAddr[i]=(unsigned char *)pav->data[i];  // capture YUV virtual addres

	enc_param.pu8BitstreamAddr=buf;
	if  ( ioctl(fmjpeg_enc_fd,FMJPEG_IOCTL_ENCODE_ONE,&enc_param) < 0 ) {
	     printf("Error to set FMJPEG_IOCTL_ENCODE_ONE\n"); 
              fflush(stdout);
	     return -1;
	}

	return enc_param.bitstream_size;	
}


int mjpeg_close(video_profile *video_setting)
{
  	if ( ioctl(fmjpeg_enc_fd,FMJPEG_IOCTL_ENCODE_DESTROY,&enc_param) < 0 ) {
	 	printf("Error to set FMJPEG_IOCTL_ENCODE_DESTROY\n");
        		fflush(stdout);
        		return -1;
        }	
        close(fmjpeg_enc_fd);	
        return 0;
}


int quality_cnt;
int main(int argc, char **argv)
{
    int i;    
    AVFrame             yuv;
    int length,fcount=0,tlength;

    dout=fopen(dout_name,"wb");
    printf("Use encoder output name %s\n",dout_name);
    		printf("2222222222222222\n");
    if(argc<2)
        exit(0);
    printf("Input File Name %s\n",argv[1]);
    din=fopen(argv[1],"rb");
	
		printf("333333333333333333333333333\n");
    //set the default value
    video_setting.qmax = 31;
    video_setting.qmin = 1;
    video_setting.quant = 0;
    video_setting.bit_rate = 512;
    video_setting.width = TEST_WIDTH;
    video_setting.height = TEST_HEIGHT;
    video_setting.framerate = 5;
    video_setting.frame_rate_base = 1;
    video_setting.gop_size = 30;
    video_setting.fmjpeg_image_quality=0;      //need assign by UI
    video_setting.u32RestartInterval=10;        //need assign by UI
    video_setting.fmjpeg_jpeg=1;                 //0 for motion JPEG, 1 for JPEG
    video_setting.fmjpeg_yuv_format=0;           //0 for YUV420
    		printf("4444444444\n");
    out_virt_buffer = (unsigned char *) malloc(TEST_WIDTH * TEST_HEIGHT * 3/2);	
 		printf("555555555555\n");
    if(mjpeg_init(&video_setting)<0)
		{
				printf("mjpeg init error\n");
        return -1;
		}
		printf("66666666666666666\n");
    mmap_addr = (int)mmap(0, (TEST_WIDTH * TEST_HEIGHT * 3*2)/2,PROT_READ|PROT_WRITE,MAP_SHARED,fmjpeg_enc_fd, 0);
    if(mmap_addr <= 0)
		{
				printf("mmap error\n");
        return -1;
		}

    fread((void *)mmap_addr,((TEST_WIDTH * TEST_HEIGHT*3)/2),1,din);
    fread((void *)mmap_addr+((TEST_WIDTH * TEST_HEIGHT*3)/2),(TEST_WIDTH * TEST_HEIGHT*3)/2,1,din);	
    printf("first YUV base 0x%x second YUV base 0x%x\n", mmap_addr, mmap_addr+((TEST_WIDTH * TEST_HEIGHT*3)/2) );
			printf("88888888888888888888\n");
    time_start(&t1);
    quality_cnt = 0;
    tlength=0;
    fcount=1;
    while(1)  {
        int offset=0;		
        i=TEST_ROUND;
        while(i--)  {
            if((fcount%20)==0)   {
                //printf("==>Round %d\n",TEST_ROUND-i);
                nsecs = time_stop(&t1) / 1000;
                printf("[%06d] Spend %06lld ms, %ffps, br=%dKbps on quality %d\n",
			fcount,nsecs,
			(float)((float)fcount)/((float)nsecs/(float)1000.0),
			((tlength/fcount)*8*30)/1000,
			enc_param.u32ImageQuality);
                fflush(stdout);
	       tlength=0;	
	       fcount=0;	   
	       quality_cnt ++;
	       if (( quality_cnt %5) == 0) {	   
	       		enc_param.u32ImageQuality = video_setting.fmjpeg_image_quality + quality_cnt;		
			if ( quality_cnt > 99 )
				quality_cnt = 0;
	       }
            }  
            fcount++;
            
            offset=fcount%2;
            yuv.data[0]=(unsigned char *)mmap_addr+(((TEST_WIDTH*TEST_HEIGHT*3)/2)*offset);
            yuv.data[1]=(unsigned char *)(mmap_addr+(((TEST_WIDTH*TEST_HEIGHT*3)/2)*offset)+(video_setting.width*video_setting.height));
            yuv.data[2]=(unsigned char *)(mmap_addr+(((TEST_WIDTH*TEST_HEIGHT*3)/2)*offset)+(video_setting.width*video_setting.height)+(video_setting.width*video_setting.height/4));
			
            
            length = mjpeg_encode(&video_setting, out_virt_buffer, (void *)&yuv);	
	   //printf("out bs len %d total %d\n", length, tlength);	
	   //fflush(stdout);
            tlength+=length;
        }
    }
    		printf("ccccccccccccc\n");
    nsecs = time_stop(&t1) / 1000;
    printf("[%06d] Spend %06lld ms, %ffps, br=%dKbps\n",fcount,nsecs,(float)((float)fcount)/((float)nsecs/(float)1000.0),((tlength/fcount)*8*30)/1000);
    fflush(stdout);
  
    if ( mjpeg_close(&video_setting) < 0 ) {
        printf("mjpeg_close fail\n");
        fflush(stdout);
    }

    fclose(din); 
    fclose(dout);
    free(out_virt_buffer);	
    
    return 0;
}
